#include "GLRenderTarget2D.h"

#include <Rz/Graphics/Math/Size.h>
#include <Rz/Graphics/Render/Libraries/OpenGL/GL.h>

namespace Rz { namespace Graphics { namespace Render { namespace Libraries { namespace OpenGL {

GLRenderTarget2D::GLRenderTarget2D(IRenderDevice* renderDevice, const Math::Size& size, SurfaceFormat preferredFormat, DepthFormat preferredDepthFormat, RenderTargetUsage usage)
	: GLTexture2D(renderDevice, static_cast<u32>(size.GetWidth()), static_cast<u32>(size.GetHeight()), false, preferredFormat)
	, _framebufferHandle(static_cast<GLuint>(-1))
	, _renderbufferHandle(static_cast<GLuint>(-1))
{
	RZ_UNUSED(usage);

	_depthFormat = preferredDepthFormat;
	_glDepthFormat = DepthFormatConversion(preferredDepthFormat);

	if (preferredDepthFormat == DepthFormat::None)
	{
		return;
	}

	_renderbufferHandle = GL::GenRenderbuffer();
	GL::BindRenderbuffer(_renderbufferHandle);
	GL::RenderbufferStorage(_glDepthFormat, static_cast<GLsizei>(size.GetWidth()), static_cast<GLsizei>(size.GetHeight()));
}

GLRenderTarget2D::~GLRenderTarget2D()
{
	if (_renderbufferHandle != static_cast<GLuint>(-1))
	{
		GL::DeleteRenderbuffer(_renderbufferHandle);
	}

	if (_framebufferHandle != static_cast<GLuint>(-1))
	{
		GL::DeleteRenderbuffer(_framebufferHandle);
	}
}

// IBindableResource
void GLRenderTarget2D::Bind()
{
	if (_framebufferHandle != static_cast<GLuint>(-1))
	{
		GL::BindFramebuffer(_framebufferHandle);
	}
}

void GLRenderTarget2D::Unbind()
{
	GL::BindFramebuffer(0);
}

void GLRenderTarget2D::SetFramebufferHandle(GLuint handle, u32 index)
{
	_framebufferHandle = handle;

	if (_depthFormat != DepthFormat::None)
	{
		GL::BindRenderbuffer(_renderbufferHandle);
		GL::FramebufferRenderbuffer(GL_DEPTH_ATTACHMENT, _renderbufferHandle);

		// TODO: stencil buffer
		// if (_depthFormat == DepthFormat::Depth24Stencil8)
		// {
		// 	GL::FramebufferRenderbuffer(GL_STENCIL_ATTACHMENT, _renderbufferHandle);
		// }
	}

	this->GLTexture2D::Bind();
	GL::FramebufferTexture2D(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), GL_TEXTURE_2D, this->GetTextureHandle(), 0);
}

GLuint GLRenderTarget2D::GetFramebufferHandle() const
{
	return _framebufferHandle;
}

GLenum GLRenderTarget2D::DepthFormatConversion(DepthFormat depthFormat)
{
	switch(depthFormat)
	{
		default:
		case DepthFormat::None:
			return GL_DEPTH_COMPONENT16;

		case DepthFormat::Depth16:
			return GL_DEPTH_COMPONENT16;

		case DepthFormat::Depth24:
			return GL_DEPTH_COMPONENT24;

		case DepthFormat::Depth24Stencil8:
			return GL_DEPTH24_STENCIL8;
	}
}

} } } } }
